package ru.spb.osll.myfit.server.handler;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import ru.spb.osll.myfit.common.Include;
import ru.spb.osll.myfit.common.IncludeError;
import ru.spb.osll.myfit.common.Page;
import ru.spb.osll.myfit.common.PageElement;
import ru.spb.osll.myfit.common.PageError;
import ru.spb.osll.myfit.common.SetUp;
import ru.spb.osll.myfit.common.TearDown;
import ru.spb.osll.myfit.common.TextParsingTools;
import ru.spb.osll.myfit.server.Converter;
import ru.spb.osll.myfit.server.parser.PageParserNew;
import ru.spb.osll.myfit.server.parser.PageParserUtils;
import ru.spb.osll.myfit.server.parser.RecursiveIncludeException;
import ru.spb.osll.myfit.util.IOUtils;

public class PageLoader {
  private Logger LOG = Logger.getLogger(PageLoader.class);
  protected PageParserNew m_parser = new PageParserNew(new Converter());
  private String m_root;

  public PageLoader(String root) {
    m_root = root;
  }

  public Page loadPage(String fileName, String pagePath, String includePath)
      throws IOException {
    Page page = new Page();
    List<String> lines;
    try {
      lines = loadPage(fileName);
      List<PageElement> pageElements = m_parser
          .parsePage(new ArrayList<String>(lines));
      List<String> allPath = new ArrayList<String>();

      for (PageElement element : pageElements) {
        allPath.clear();
        if (!(element instanceof Include)) {
          page.insert(element);
        } else {
          allPath.add(pagePath);
          try {
            Page inludePage = createInlcudePage(((Include) element).getPath(),
                allPath);
            ((Include) element).setPage(inludePage);
            page.insert(element);
          } catch (RecursiveIncludeException exeption) {
            String message = TextParsingTools.RECURSIVE_CALL + pagePath +
                " from the page " + exeption.getMessage();
            page.insert(new IncludeError(((Include) element).getPath(),
                createPageWithError(message)));
          } catch (FileNotFoundException e) {
            page.insert(new IncludeError(((Include) element).getPath(),
                createPageWithError(e.getMessage())));
          }
        }
      }

      // add setUp in created page
      allPath.clear();
      allPath.add(pagePath);
      SetUp setUp = addSetUp(pagePath, allPath);
      if (setUp != null) {
        page.insertElement(setUp, 0);
      }

      // add TearDown in created page
      allPath.clear();
      allPath.add(pagePath);
      TearDown tearDown = addTearDown(pagePath, allPath);
      if (tearDown != null) {
        page.insert(tearDown);
      }
    } catch (FileNotFoundException e) {
      LOG.info("File not found: " + fileName + ". Load new page");
    }

    return page;
  }

  private SetUp addSetUp(String pagePath, List<String> allPath) {
    if (isSetUpOrTearDown(pagePath)) {
      return null;
    }

    String includePath;
    if (pagePath.contains(".")) {
      int beginIndex = pagePath.lastIndexOf(".");
      String rootPath = pagePath.substring(0, beginIndex + 1);
      includePath = rootPath + TextParsingTools.SETUP;
      if (!includePath.startsWith(".")) {
        includePath = "." + includePath;
      }
    } else {
      includePath = "." + TextParsingTools.SETUP;
    }

    try {
      Page page = createInlcudePage(includePath, allPath);
      return new SetUp(includePath, page);
    } catch (FileNotFoundException e) {
      LOG.debug("SetUp for page " + pagePath + " not found");
    } catch (RecursiveIncludeException e) {
      LOG.debug("SetUp for page " + pagePath + " isRecursive");
    }
    return null;
  }

  private TearDown addTearDown(String pagePath, List<String> allPath) {
    if (isSetUpOrTearDown(pagePath)) {
      return null;
    }

    String includePath;
    if (pagePath.contains(".")) {
      int beginIndex = pagePath.lastIndexOf(".");
      String rootPath = pagePath.substring(0, beginIndex + 1);
      includePath = rootPath + TextParsingTools.TEARDOWN;
      if (!includePath.startsWith(".")) {
        includePath = "." + includePath;
      }
    } else {
      includePath = "." + TextParsingTools.TEARDOWN;
    }

    try {
      Page page = createInlcudePage(includePath, allPath);
      return new TearDown(includePath, page);
    } catch (FileNotFoundException e) {
      LOG.debug("TearDown for page " + pagePath + " not found");
    } catch (RecursiveIncludeException e) {
      LOG.debug("TearDown for page " + pagePath + " isRecursive");
    }
    return null;
  }

  private boolean isSetUpOrTearDown(String pagePath) {
    String path;
    if (pagePath.contains(".")) {
      int beginIndex = pagePath.lastIndexOf(".");
      path = pagePath.substring(beginIndex + 1);
    } else {
      path = pagePath;
    }

    if (path.equals(TextParsingTools.SETUP)) {
      return true;
    }
    if (path.equals(TextParsingTools.TEARDOWN)) {
      return true;
    }

    return false;
  }

  public Page createPageWithError(String errorMessage) {
    Page result = new Page();
    result.insert(createPageError(errorMessage));

    return result;
  }

  private PageError createPageError(String msg) {
    PageError pageError = new PageError();
    pageError.setMessage(msg);
    return pageError;
  }

  public Page createInlcudePage(String includePath, List<String> allPath)
      throws RecursiveIncludeException, FileNotFoundException {
    Page page = new Page();
    if (isRecursiveInclude(includePath, allPath)) {
      allPath.add(includePath);
      throw new RecursiveIncludeException(getRecursivePath(allPath));
    } else {
      try {
        List<String> lines = loadPage(PageParserUtils.getFullPath(m_root,
            includePath));
        List<PageElement> pageElements = m_parser
            .parsePage(new ArrayList<String>(lines));
        for (PageElement element : pageElements) {

          if (!(element instanceof Include)) {
            page.insert(element);
          } else {
            ArrayList<String> forNextInclude = new ArrayList<String>(allPath);
            forNextInclude.add(includePath);
            try {
              Page inludePage = createInlcudePage(
                  ((Include) element).getPath(), forNextInclude);
              ((Include) element).setPage(inludePage);
              page.insert(element);
            } catch (RecursiveIncludeException exeption) {
              throw exeption;
            } catch (FileNotFoundException e) {
              page.insert(new IncludeError(((Include) element).getPath(),
                  createPageWithError(e.getMessage())));
            }
          }
        }

        ArrayList<String> forSetUp = new ArrayList<String>(allPath);
        forSetUp.addAll(allPath);
        SetUp setUp = addSetUp(includePath, forSetUp);
        if (setUp != null) {
          page.insertElement(setUp, 0);
        }

        forSetUp.clear();
        forSetUp.addAll(allPath);
        TearDown tearDown = addTearDown(includePath, forSetUp);
        if (tearDown != null) {
          page.insert(tearDown);
        }

      } catch (FileNotFoundException e) {
        throw e;
      } catch (IOException e) {
        LOG.error("Can't read page ", e);
      }
    }
    return page;
  }

  private String getRecursivePath(List<String> allPath) {
    String result = new String();
    int size = allPath.size();
    for (int i = 1; i < size; ++i) {
      result += allPath.get(i);
      result += (i != size - 1) ? " ⇾ " : " ";
    }
    return result;
  }

  private boolean isRecursiveInclude(String includePath, List<String> allPaths) {
    if (allPaths.contains(includePath)) {
      return true;
    }

    if (includePath.startsWith(".")) {
      String pathWithoutPoint = includePath.substring(1);
      if (allPaths.contains(pathWithoutPoint)) {
        return true;
      }
    } else {
      String pathWithPoint = "." + includePath;
      if (allPaths.contains(pathWithPoint)) {
        return true;
      }
    }
    return false;
  }

  public List<String> loadPage(String fileName) throws IOException,
      FileNotFoundException {
    LOG.info("Loading page, path: " + fileName);
    FileInputStream stream = null;
    try {
      stream = new FileInputStream(fileName);
      BufferedReader inStream = new BufferedReader(
          new InputStreamReader(stream));
      return IOUtils.readLines(inStream);
    } finally {
      //LOG.debug("file don't exist in finally");
      if (stream != null)
        stream.close();
    }
  }
}
